<?php
namespace doujunyu\utility\tencen\wechat;

use think\Exception;

/**
 * 小程序登录使用的接口，只需要传入对应的参数，即可返回数据。
 */
class WeChat
{
    protected $appid = '';
    protected $secret = '';

    // |---------------------------------
    // | 这里分为公众号和小程序
    // | 两个都用到使用 all 开头
    // | 公众号 offiaccount（offi） 开头
    // | 小程序 miniprogram (mini) 开头
    // |---------------------------------
    protected const all_access_token_url = 'https://api.weixin.qq.com/cgi-bin/token';//获取access_token

    protected const miniprogram_send_login_url = 'https://api.weixin.qq.com/sns/jscode2session';//登录
    protected const miniprogram_get_phone_url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber';//获取用户手机号

    public function __construct(string $appid, string $secret)
    {
        $this->appid = $appid;
        $this->secret = $secret;
    }



    /**
     * 获取access_token
     * @return array
     */
    public function all_access_token(){
        $data = [];
        $send_data = [
            'appid' => $this->appid,
            'secret' => $this->secret,
            'grant_type'=>'client_credential',
        ];
        try {
            $back_data = self::callApi('POST',self::all_access_token_url,$send_data);
            $back_data = json_decode($back_data,true);
            if(!$back_data){
                throw new Exception('获取access_token的时候请求失败');
            }
            //返回成功和失败都会有errcode，且成功的时候是0，则这里不是0则说明有错误信息
            if(isset($back_data['errcode']) ){
                throw new Exception($back_data['errmsg'] ?? '获取access_token时未找到errmsg字段');
            }
            $data['access_token'] = $back_data['access_token'] ?? '';
        } catch (\Exception $e) {
            // 回滚事务
            return [$e->getMessage(),''];
        }
        return ['',$data];
    }

    // |---------------------------------
    // | 小程序接口
    // |---------------------------------

    /**
     * 小程序登录
     * @param string $js_code 前端获取到的code
     * @return array
     */
    public function miniprogram_send_login(string $js_code){
        $data = [];
        try {
            $send_data = [
                'appid' => $this->appid,
                'secret' => $this->secret,
                'js_code' => $js_code,
                'grant_type' => 'authorization_code',
            ];
            //请求接口
            $back_data = self::callApi('POST',self::miniprogram_send_login_url,$send_data);
            if(!$back_data){
                throw new Exception('小程序登录的时候请求失败');
            }
            $back_data = json_decode($back_data,true);
            //因为只有失败的时候才会返回errcode，则errcode只要存在，肯定有错误信息
            if(isset($back_data['errcode']) ){
                throw new Exception($back_data['errmsg'] ?? '小程序登录时未找到errmsg字段');
            }
            $data['session_key'] = $back_data['session_key'] ?? '';
            $data['unionid'] = $back_data['unionid'] ?? '';//用户在开放平台的唯一标识符，若当前小程序已绑定到微信开放平台账号下会返回
            $data['openid'] = $back_data['openid'] ?? '';
        } catch (\Exception $e) {
            // 回滚事务
            return [$e->getMessage(),''];
        }
        return ['',$data];
    }

    /**
     * 获取手机号
     * @param string $access_token 调用all_access_token方法获取到的token
     * @param string $code 获取手机号的code,不是登录的code
     * @return array
     */
    public function miniprogram_get_phone(string $access_token,string $code){
        $data = [];
        try {
            $send_data = [
                'code' => $code,
            ];
            //请求接口
            $options = array(
                'http' => array(
                    'header' => "Content-Type: application/json\r\n",
                    'method' => 'POST',
                    'content' => json_encode($send_data)
                )
            );
            //这个地方不能使用下面的callApi的post方法，因为这个地方的请求头是application/json格式
            $url = self::miniprogram_get_phone_url."?access_token=".$access_token;
            $context = stream_context_create($options);
            $response = file_get_contents($url, false, $context);
            $back_data = json_decode($response, true);
            if(!$back_data){
                throw new Exception('小程序获取手机号的时候请求失败');
            }
            //返回成功和失败都会有errcode，且成功的时候是0，则这里不是0则说明有错误信息
            if($back_data['errcode'] != 0){
                throw new Exception($back_data['errmsg'] ?? '获取手机号时未找到errmsg字段');
            }
            $data['phoneNumber'] = $back_data['phone_info']['phoneNumber'] ?? '';
            $data['purePhoneNumber'] = $back_data['phone_info']['purePhoneNumber'] ?? '';
            $data['countryCode'] = $back_data['phone_info']['countryCode'] ?? '';
        } catch (\Exception $e) {
            // 回滚事务
            return [$e->getMessage(),''];
        }
        return ['',$data];
    }


    // |---------------------------------
    // | 公众号接口
    // |---------------------------------

    /**
     * 公众号授权登录
     * @param string $code
     * @return array
     */
    public function offiaccount_send_log(string $code){
        $data = [];
        try {
            $send_data = [
                'appid' => $this->appid,
                'secret' => $this->secret,
                'code' => $code,
                'grant_type' => 'authorization_code',
            ];
            //请求接口
            $back_data = self::callApi('GET','https://api.weixin.qq.com/sns/oauth2/access_token',$send_data);
            if(!$back_data){
                throw new Exception('公众号登录的时候请求失败');
            }
            $back_data = json_decode($back_data,true);
            //因为只有失败的时候才会返回errcode，则errcode只要存在，肯定有错误信息
            if(isset($back_data['errcode']) ){
                throw new Exception($back_data['errmsg'] ?? '公众号登录时未找到errmsg字段');
            }
            $data['access_token'] = $back_data['access_token'] ?? '';
            $data['unionid'] = $back_data['unionid'] ?? '';//用户在开放平台的唯一标识符，若当前小程序已绑定到微信开放平台账号下会返回
            $data['openid'] = $back_data['openid'] ?? '';
        } catch (\Exception $e) {
            // 回滚事务
            return [$e->getMessage(),''];
        }
        return ['',$data];
    }

    /**
     * 拉取用户信息(需scope为 snsapi_userinfo)
     * @param string $access_token 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
     * @param string $open_id 用户的唯一标识
     * @return array
     */
    public function offiaccount_user_info(string $access_token,string $open_id){
        $data = [];
        try {
            $send_data = [
                'access_token' => $access_token,
                'openid' => $open_id,
                'lang' => 'zh_CN',
            ];
            //请求接口
            $back_data = self::callApi('GET','https://api.weixin.qq.com/sns/userinfo',$send_data);
            if(!$back_data){
                throw new Exception('公众号登录的时候请求失败');
            }
            $back_data = json_decode($back_data,true);
            //因为只有失败的时候才会返回errcode，则errcode只要存在，肯定有错误信息
            if(isset($back_data['errcode']) ){
                throw new Exception($back_data['errmsg'] ?? '公众号登录时未找到errmsg字段');
            }
            $data['nickname'] = $back_data['nickname'] ?? '';//用户昵称
            $data['sex'] = $back_data['sex'] ?? '';//用户的性别，值为1时是男性，值为2时是女性，值为0时是未知
            $data['province'] = $back_data['province'] ?? '';//用户个人资料填写的省份
            $data['city'] = $back_data['city'] ?? '';//普通用户个人资料填写的城市
            $data['country'] = $back_data['country'] ?? '';//国家，如中国为CN
            $data['headimgurl'] = $back_data['headimgurl'] ?? '';//用户头像
            $data['privilege'] = $back_data['privilege'] ?? '';//用户特权信息，json 数组，如微信沃卡用户为（chinaunicom）
            $data['unionid'] = $back_data['unionid'] ?? '';//只有在用户将公众号绑定到微信开放平台账号后，才会出现该字段。
        } catch (\Exception $e) {
            // 回滚事务
            return [$e->getMessage(),''];
        }
        return ['',$data];

    }






    // |---------------------------------
    // | 下面是杂项
    // |---------------------------------

    /**
     * http_build_query 请求方法
     * @param string $method 请求方式，大写
     * @param string $url 请求路径
     * @param array $data 请求数据
     * @return bool|string
     */
    protected static function callApi(string $method, string $url,array $data = []) {
        $ch = curl_init();
        switch ($method) {
            case "GET":
                $getData = http_build_query($data);
                $fullUrl = $url . "?" . $getData;
                curl_setopt($ch, CURLOPT_URL, $fullUrl);
                break;
            case "POST":
                curl_setopt($ch, CURLOPT_URL, $url);
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

                break;
            default:
                return false;
        }

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }

}